#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>

#define	DE_DIR	4
#define	DE_FILE	8
#define	DE_LINK	10

#define FLFS_NODE_NONE 0
#define FLFS_NODE_ROOT 1
#define FLFS_NODE_DIR 2
#define FLFS_NODE_FILE 3
#define FLFS_NODE_DATA 4

struct FLFS_BLOCK
{
	char type;
	char data[495];
	long long child;
	long long next;
};

struct t_originalfs
{
	char name[495];
	char namePath[495];
	int next;
	int child;
	int type;
	int sector;
};

t_originalfs ofs[1000];
int ofsCount = 0;

void parsedir(char *path, int parent = -1)
{
	DIR *dir;
	struct dirent *dp;
	static int level = 0;
	static int id = 0;
	int i;

	char tmp[256];
	int d_type;

	level++;

	int lastInLevel = 0;

	if((dir = opendir(path)) == NULL)
	{
		printf("Could not open %s.\n", path);
	}
	do
	{
		if((dp = readdir(dir)) != NULL)
		{
			if(dp->d_name[0] == '.') continue;

			// next ID
			id++;

			if(id>ofsCount) ofsCount = id;

			for(i=0;i<level;i++) printf("\t");
//			stat(dp->d_name);

			sprintf(tmp, "%s/%s", path, dp->d_name);
			printf("%s", tmp);
			DIR *h;
			h=opendir(tmp);
			if(!h) d_type=DE_FILE;
			else d_type=DE_DIR;
			closedir(h);

			printf("%s", (d_type==DE_FILE)?"[F] ":"[D] ");
			(void)printf("(ID:%d) ", id);
			if((parent!=-1) && !lastInLevel)
			{
				printf("(Parent:%d) ", parent);
				ofs[parent].child = id;
			}
			if(lastInLevel)
			{
				printf("(Previous:%d) ", lastInLevel);
				ofs[lastInLevel].next = id;
			}
			(void)printf("%s\n", dp->d_name);

			strcpy(ofs[id].name, dp->d_name);
			sprintf(tmp, "%s/%s", path, dp->d_name);
			strcpy(ofs[id].namePath, tmp);
			ofs[id].next = 0;
			ofs[id].child = 0;

//			printf("Changing lastInLevel to %d...\n", id);
			lastInLevel = id;

			if(d_type == DE_FILE)
			{
				ofs[id].type = FLFS_NODE_FILE;
			}
			else
			{
				ofs[id].type = FLFS_NODE_ROOT;
				sprintf(tmp, "%s/%s", path, dp->d_name);
//				(void)printf("%d: %s\n", id, tmp);
				parsedir(tmp, id);
			}
		}
	}
	while(dp != NULL);
	level--;
	return;
}

void makeFS(char *fname, int offset)
{
	int i, k;
	FLFS_BLOCK *hdd;
	int RAM = 32;	// in MB
	hdd = (FLFS_BLOCK*) malloc(sizeof(FLFS_BLOCK)*2*1024*RAM);	// 32MB
	if(hdd == NULL) printf(" *** Not enough memory! Couldn't allocate 32MB %d RAM.", RAM);
	int currentBlock = 0;

	char buf[495];
	char *data;
	int fsize;

	for(i=0;i<=ofsCount;i++)
	{
		strcpy(hdd[currentBlock].data, ofs[i].name);
		hdd[currentBlock].type = ofs[i].type;
		hdd[currentBlock].child = ofs[i].child+offset;
		hdd[currentBlock].next = ofs[i].next+offset;
		ofs[i].sector = currentBlock+offset;
		for(k=1;k<i;k++)
		{
			if(ofs[k].next == i) { ofs[k].next = -(currentBlock+offset); printf("CHANGED NEXT to %d\n", i); }
			if(ofs[k].child == i) { ofs[k].child = -(currentBlock+offset); printf("CHANGED CHILD to %d\n", i); }
		}
		currentBlock++;
		if(ofs[i].type == FLFS_NODE_FILE)
		{
			// read file in 495-b sectors
			FILE *fp;
			fp = fopen(ofs[i].namePath, "r");
			bool firstSector = true;
			while(!feof(fp))
			{
				if(!firstSector) hdd[currentBlock-1].next = currentBlock+offset;
				hdd[currentBlock].type = FLFS_NODE_DATA;
				hdd[currentBlock].next = 0;
				fread(hdd[currentBlock++].data, 495, 1, fp);
				firstSector = false;
			}
			fclose(fp);
		}
	}

	for(i=0;i<=ofsCount;i++)
	{
		ofs[i].next=abs(ofs[i].next);
		ofs[i].child=abs(ofs[i].child);
		printf("   - %d (sector %d): %s (%s)", i, ofs[i].sector, ofs[i].name, ofs[i].namePath);
		if(ofs[i].child) printf(" | child: %d", ofs[i].child);
		if(ofs[i].next) printf(" | next: %d", ofs[i].next);
		printf("\n");
	}

	printf(" > Total files/folders (including ROOT): %d\n", ofsCount+1);
	printf(" > Total sectors: %d\n", currentBlock);
	printf(" > Writing HDD image...\n");
	FILE *outfp;
	outfp = fopen(fname, "w");
	fwrite(hdd, 512, currentBlock, outfp);
	fclose(outfp);
	free(hdd);
}

int main(int argc, char *argv[])
{
	char fname[256];
//	printf("%d", sizeof(FLFS_BLOCK));
	if((argc != 3) && (argc != 4))
	{
		printf("FLoating File System image generator\nUsage: fsgen <virtual root directory> <output file> [offset]\n");
		exit(1);
	}

	strcpy(ofs[0].name, "@");
	strcpy(ofs[0].namePath, argv[1]);
	ofs[0].next = 0;
	ofs[0].type = FLFS_NODE_ROOT;
	// child will be assigned later

	printf(" > Parsing folder...\n");
	parsedir(argv[1]);

	printf("\n > Converting FS into FLFS array...\n");
	if(strstr(argv[2], ".hdd")) strcpy(fname, argv[2]);
	else sprintf(fname, "%s.hdd", argv[2]);

	makeFS(fname, (argc==4)?atoi(argv[3]):0);
	printf(" > Written to file: %s\n", fname);
	printf(" > All done!\n");

	return 0;
}
